using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Game2048WithCSharp
{
    public interface ILogic
    {
        int[,] NewGame(out int currentScore, out int bestScore);
        CubeMove[] MoveCubes(Direction direction,out GameState gameState, out int currentScore, out int bestScore, out bool moved);
        int[,] GenerateNewCube(out GameState gameState, bool moved);
    }
    [Obsolete]
    public class Game2048 : ILogic
    {
        private const int BoardSize = 4;
        private Random random = new Random();
        // set public for test
        public int[,] board;
        private int currentScore;
        private int bestScore;
        private GameState gameState;
        private bool moved;

        public int[,] NewGame(out int currentScore, out int bestScore)
        {
            board = new int[BoardSize, BoardSize];
            currentScore = 0;
            bestScore = this.bestScore;
            gameState = GameState.Gaming;

            // 随机生成两个2或4
            GenerateNewCube(out gameState, true);
            GenerateNewCube(out gameState, true);

            return board;
        }

        public CubeMove[] MoveCubes(Direction direction, out GameState gameState, out int currentScore, out int bestScore, out bool moved)
        {
            moved = false;
            List<CubeMove> moves = new List<CubeMove>();

            for (int i = 0; i < BoardSize; i++)
            {
                int[] line = GetLine(i, direction);
                int[] mergedLine = MergeLine(MoveLine(line, ref moved));
                SetLine(i, mergedLine, direction, ref moves);
            }

            
            gameState = CheckGameState();
            currentScore = this.currentScore;
            bestScore = Math.Max(this.bestScore, currentScore);

            return moves.ToArray();
        }

        public int[,] GenerateNewCube(out GameState gameState, bool moved)
        {

            List<(int, int)> emptyTiles = new List<(int, int)>();
            int[,] addboard = new int[BoardSize, BoardSize];

            if ( !moved )
            {
                gameState = CheckGameState();
                return addboard;
            }

            for (int i = 0; i < BoardSize; i++)
            {
                for (int j = 0; j < BoardSize; j++)
                {
                    if (board[i, j] == 0)
                    {
                        emptyTiles.Add((i, j));
                    }
                }
            }

            if (emptyTiles.Count > 0)
            {
                var (x, y) = emptyTiles[random.Next(emptyTiles.Count)];
                // 10%概率生成4，90%概率生成2
                addboard[x, y] = random.Next(10) == 0 ? 4 : 2;
                board[x, y] = addboard[x, y];
            }
            gameState = CheckGameState();
            return addboard;
        }

      
        // 获取第index行（或列）
        private int[] GetLine(int index, Direction direction)
        {
            int[] line = new int[BoardSize];
            for (int i = 0; i < BoardSize; i++)
            {
                switch (direction)
                {
                    case Direction.Up:
                        line[i] = board[i, index];
                        break;
                    case Direction.Down:
                        line[i] = board[BoardSize - 1 - i, index];
                        break;
                    case Direction.Left:
                        line[i] = board[index, i];
                        break;
                    case Direction.Right:
                        line[i] = board[index, BoardSize - 1 - i];
                        break;
                }
            }
            return line;
        }

        // 更新board，统计moves
        private void SetLine(int index, int[] line, Direction direction, ref List<CubeMove> moves)
        {
            int idx_line = 0;
            for (int i = 0; i < BoardSize; i++)
            {
                switch (direction)
                {
                    case Direction.Up:
                        if (board[i, index] == 0)
                        {
                            board[i, index ] = line[i];
                        }

                        else if (board[i, index] == line[idx_line])
                        {
                            moves.Add(new CubeMove { OldX = i, OldY = index, NewX = idx_line, NewY = index });
                            idx_line++;
                            board[i, index] = line[i];
                        }

                        else
                        {
                            moves.Add(new CubeMove { OldX = i, OldY = index, NewX = idx_line, NewY = index });
                            moves.Add(new CubeMove { OldX = i + 1, OldY = index, NewX = idx_line, NewY = index });
                            idx_line++;
                            board[i, index] = line[i];
                            board[i + 1, index] = line[i + 1];
                            i++;
                        }
                        break;

                    case Direction.Down:
                        if (board[BoardSize - 1 - i, index] == 0)
                        {
                            board[BoardSize - 1 - i, index] = line[i];
                        }

                        else if (board[BoardSize - 1 - i, index] == line[idx_line])
                        {
                            moves.Add(new CubeMove { OldX = BoardSize - 1 - i, OldY = index, NewX = BoardSize - 1 - index, NewY = index });
                            idx_line++;
                            board[BoardSize - 1 - i, index] = line[i];
                        }

                        else
                        {
                            moves.Add(new CubeMove { OldX = BoardSize - 1 - i, OldY = index, NewX = BoardSize - 1 - idx_line, NewY = index });
                            moves.Add(new CubeMove { OldX = BoardSize - 2 - i, OldY = index, NewX = BoardSize - 1 - idx_line, NewY = index });
                            idx_line++;
                            board[BoardSize - 1 - i, index] = line[i];
                            board[BoardSize - 2 - i, index] = line[i + 1];
                            i++;
                        }
                        break;

                    case Direction.Left:
                        if (board[index, i] == 0)
                        {
                            board[index, i] = line[i];
                        }

                        else if (board[index, i] == line[idx_line])
                        {
                            moves.Add(new CubeMove { OldX = index, OldY = i, NewX = index, NewY = idx_line });
                            idx_line++;
                            board[index, i] = line[i];
                        }

                        else
                        {
                            moves.Add(new CubeMove { OldX = index, OldY = i, NewX = index, NewY = idx_line });
                            moves.Add(new CubeMove { OldX = index, OldY = i + 1, NewX = index, NewY = idx_line });
                            idx_line++;
                            board[index, i] = line[i];
                            board[index, i + 1] = line[i + 1];
                            i++;
                        }
                        break;

                    case Direction.Right:
                        if (board[index, BoardSize - 1 - i] == 0)
                        {
                            board[index, BoardSize - 1 - i] = line[i];
                        }

                        else if (board[index, BoardSize - 1 - i] == line[idx_line])
                        {
                            moves.Add(new CubeMove { OldX = index, OldY = BoardSize - 1 - i, NewX = index, NewY = BoardSize - 1 - idx_line });
                            idx_line++;
                            board[index, BoardSize - 1 - i] = line[i];
                        }

                        else
                        {
                            moves.Add(new CubeMove { OldX = index, OldY = BoardSize - 1 - i, NewX = index, NewY = BoardSize - 1 - idx_line });
                            moves.Add(new CubeMove { OldX = index, OldY = BoardSize - 2 - i, NewX = index, NewY = BoardSize - 1 - idx_line });
                            idx_line++;
                            board[index, BoardSize - 1 - i] = line[i];
                            board[index, BoardSize - 2 - i] = line[i + 1];
                            i++;
                        }
                        break;
                }
            }
        }

        // 移动一行（或列）的数据到一侧
        private int[] MoveLine(int[] oldLine, ref bool moved)
        {
            List<int> newLine = oldLine.Where(x => x != 0).ToList();
            while (newLine.Count < BoardSize)
            {
                newLine.Add(0);
            }

            for (int i = 0; i < oldLine.Length; i++)
            {
                if (oldLine[i] != newLine[i])
                    moved = true;
            }

            return newLine.ToArray();
        }

        // 相邻的相同数据合并，并更新score
        private int[] MergeLine(int[] oldLine)
        {
            for (int i = 0; i < oldLine.Length - 1; i++)
            {
                if (oldLine[i] != 0 && oldLine[i] == oldLine[i + 1])
                {
                    oldLine[i] *= 2;
                    currentScore += oldLine[i];
                    oldLine[i + 1] = 0;
                }
            }

            List<int> newLine = oldLine.Where(x => x != 0).ToList();
            while (newLine.Count < BoardSize)
            {
                newLine.Add(0);
            }

            return newLine.ToArray() ;
        }

        private GameState CheckGameState()
        {
            for (int i = 0; i < BoardSize; i++)
            {
                for (int j = 0; j < BoardSize; j++)
                {
                    if (board[i, j] == 2048)
                    {
                        return GameState.Victory;
                    }
                    
                }
            }

            for (int i = 0; i < BoardSize; i++)
            {
                for (int j = 0; j < BoardSize; j++)
                {
                    if (board[i, j] == 0)
                    {
                        return GameState.Gaming;
                    }
                    if (i < BoardSize - 1 && board[i, j] == board[i + 1, j])
                    {
                        return GameState.Gaming;
                    }
                    if (j < BoardSize - 1 && board[i, j] == board[i, j + 1])
                    {
                        return GameState.Gaming;
                    }
                }
            }
            return GameState.Defeat;
        }
    }
}